package org.springside.examples.miniweb.web.account;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Transient;
import javax.servlet.jsp.JspTagException;

import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springside.examples.miniweb.entity.account.User;
import org.springside.examples.miniweb.entity.report.ColdPumpReport;
import org.springside.examples.miniweb.entity.report.CompressorReport;
import org.springside.examples.miniweb.entity.search.SearchForm;
import org.springside.examples.miniweb.service.account.AccountManager;
import org.springside.examples.miniweb.service.report.ColdPumpReportManager;
import org.springside.examples.miniweb.service.report.CompReportManager;

/**
 * Urls: List page : GET /account/user/ Create page : GET /account/user/create
 * Create action : POST /account/user/save Update page : GET
 * /account/user/update/{id} Update action : POST /account/user/save/{id} Delete
 * action : POST /account/user/delete/{id} CheckLoginName ajax: GET
 * /account/user/checkLoginName?oldLoginName=a&loginName=b
 * 
 * @author calvin
 * 
 */
@Controller
@RequestMapping(value = "/account/user")
public class UserController {

	private ColdPumpReportManager<ColdPumpReport> coldPumpReportManager;
	
	private CompReportManager<CompressorReport> compReportManager;
	
	private AccountManager accountManager;

	private GroupListEditor groupListEditor;

	@InitBinder
	public void initBinder(WebDataBinder b) {
		b.registerCustomEditor(List.class, "groupList", groupListEditor);
	}

	@RequestMapping(value = { "list"})
	public String list(Model model) {
		List<User> users = accountManager.getAllUser();
		model.addAttribute("users", users);
		return "account/userList";
	}

	@RequestMapping(value = "create")
	public String createForm(Model model) {
		model.addAttribute("user", new User());
		model.addAttribute("allGroups", accountManager.getAllGroup());
		return "account/userForm";
	}

	
	@RequestMapping(value = "search")
	public String search(@ModelAttribute("search") SearchForm searchForm, Model model) {
		User user = accountManager.findUserByName(searchForm.getUserName());
		List<User> users = new ArrayList<User>();
		users.add(user);
		model.addAttribute("users", users);
		return "account/userList";
	}
	
	@RequestMapping(value = "edit/{userLoginName}")
	public String editUser(@PathVariable("userLoginName") String userLoginName,
			Model model) throws JspTagException {
		User user = accountManager.findUserByLoginName(userLoginName);
		boolean permitted = SecurityUtils.getSubject().isPermitted(
				"user:superedit");
		if (!permitted) {
			Object principal = SecurityUtils.getSubject().getPrincipal();
			String strValue = null;
			if (principal != null) {
				strValue = getPrincipalProperty(principal, "id");
				String id = user.getId() + "";
				if (!id.equals(strValue)) {
					return "redirect:/logout";
				}
			}
		}

		model.addAttribute("user", user);
		model.addAttribute("allGroups", accountManager.getAllGroup());
		model.addAttribute("editUser", true);
		return "account/userForm";
	}

	/**
	 * Create Save
	 * 
	 * @param user
	 * @param redirectAttributes
	 * @return
	 * @throws JspTagException
	 */
	@RequestMapping(value = "save")
	public String save(User user, RedirectAttributes redirectAttributes)
			throws JspTagException {
		accountManager.saveUser(user);
		redirectAttributes.addFlashAttribute("message",
				"创建用户" + user.getLoginName() + "成功");
		return "redirect:/account/user/list";
	}

	@RequestMapping(value = "delete/{id}")
	public String delete(@PathVariable("id") Long id,
			RedirectAttributes redirectAttributes) {
		User user = accountManager.getUser(id);
		List<ColdPumpReport> coldReports = coldPumpReportManager
				.getReportByUserName(user.getName());
		List<CompressorReport> compReports = compReportManager
				.getReportByUserName(user.getName());
		if (coldReports.size() != 0 || compReports.size() != 0) {
			redirectAttributes.addFlashAttribute("errormessage",
					"已经有维修记录匹配， 用户禁止删除");
		} else {
			accountManager.deleteUser(id);
			redirectAttributes.addFlashAttribute("message", "删除用户成功");
		}
		return "redirect:/account/user/list";
	}

	@RequestMapping(value = "checkLoginName")
	@ResponseBody
	public String checkLoginName(
			@RequestParam("oldLoginName") String oldLoginName,
			@RequestParam("loginName") String loginName) {
		String decodeLoginName = decodeURIComponent(loginName);
		if (loginName.equals(oldLoginName)) {
			return "true";
		} else if (accountManager.findUserByLoginName(decodeLoginName) == null) {
			return "true";
		}

		return "false";
	}

	@RequestMapping(value = "checkName")
	@ResponseBody
	public String checkName(@RequestParam("oldName") String oldName,
			@RequestParam("name") String name) {
		String decodeName = decodeURIComponent(name);
		if (oldName.equals(decodeName)) {
			return "true";
		} else if (accountManager.findUserByName(decodeName) == null) {
			return "true";
		}

		return "false";
	}

	@Autowired
	public void setAccountManager(AccountManager accountManager) {
		this.accountManager = accountManager;
	}

	/**
	 * @param coldPumpReportManager the coldPumpReportManager to set
	 */
	@Autowired
	public void setColdPumpReportManager(
			ColdPumpReportManager<ColdPumpReport> coldPumpReportManager) {
		this.coldPumpReportManager = coldPumpReportManager;
	}

	/**
	 * @param compReportManager the compReportManager to set
	 */
	@Autowired
	public void setCompReportManager(
			CompReportManager<CompressorReport> compReportManager) {
		this.compReportManager = compReportManager;
	}
	
	@Autowired
	public void setGroupListEditor(GroupListEditor groupListEditor) {
		this.groupListEditor = groupListEditor;
	}

	/**
	 * decode URI component especially for chinese character
	 * 
	 * @param encodedURI
	 * @return
	 */
	public static String decodeURIComponent(String encodedURI) {
		char actualChar;

		StringBuffer buffer = new StringBuffer();

		int bytePattern, sumb = 0;

		for (int i = 0, more = -1; i < encodedURI.length(); i++) {
			actualChar = encodedURI.charAt(i);

			switch (actualChar) {
			case '%': {
				actualChar = encodedURI.charAt(++i);
				int hb = (Character.isDigit(actualChar) ? actualChar - '0'
						: 10 + Character.toLowerCase(actualChar) - 'a') & 0xF;
				actualChar = encodedURI.charAt(++i);
				int lb = (Character.isDigit(actualChar) ? actualChar - '0'
						: 10 + Character.toLowerCase(actualChar) - 'a') & 0xF;
				bytePattern = (hb << 4) | lb;
				break;
			}
			case '+': {
				bytePattern = ' ';
				break;
			}
			default: {
				bytePattern = actualChar;
			}
			}

			// * Decode byte bytePattern as UTF-8, sumb collects incomplete
			// chars *//*
			if ((bytePattern & 0xc0) == 0x80) { // 10xxxxxx
				sumb = (sumb << 6) | (bytePattern & 0x3f);
				if (--more == 0)
					buffer.append((char) sumb);
			} else if ((bytePattern & 0x80) == 0x00) { // 0xxxxxxx
				buffer.append((char) bytePattern);
			} else if ((bytePattern & 0xe0) == 0xc0) { // 110xxxxx
				sumb = bytePattern & 0x1f;
				more = 1;
			} else if ((bytePattern & 0xf0) == 0xe0) { // 1110xxxx
				sumb = bytePattern & 0x0f;
				more = 2;
			} else if ((bytePattern & 0xf8) == 0xf0) { // 11110xxx
				sumb = bytePattern & 0x07;
				more = 3;
			} else if ((bytePattern & 0xfc) == 0xf8) { // 111110xx
				sumb = bytePattern & 0x03;
				more = 4;
			} else { // 1111110x
				sumb = bytePattern & 0x01;
				more = 5;
			}
		}
		return buffer.toString();
	}
	
	@Transient
	 private String getPrincipalProperty(Object principal, String property) throws JspTagException {
	        String strValue = null;

	        try {
	            BeanInfo bi = Introspector.getBeanInfo(principal.getClass());

	            // Loop through the properties to get the string value of the specified property
	            boolean foundProperty = false;
	            for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
	                if (pd.getName().equals(property)) {
	                    Object value = pd.getReadMethod().invoke(principal, (Object[]) null);
	                    strValue = String.valueOf(value);
	                    foundProperty = true;
	                    break;
	                }
	            }

	            if (!foundProperty) {
	                final String message = "Property [" + property + "] not found in principal of type [" + principal.getClass().getName() + "]";
	                throw new JspTagException(message);
	            }

	        } catch (Exception e) {
	            final String message = "Error reading property [" + property + "] from principal of type [" + principal.getClass().getName() + "]";
	            throw new JspTagException(message, e);
	        }

	        return strValue;
	    }
}
